package api

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"go-caipu/pkg/api/jwt"
	"go-caipu/pkg/i18n"
	"go-caipu/pkg/infra/snowflake"
	"go-caipu/pkg/services/admin/systemlog"
	"go-caipu/pkg/services/monitor/onlineuser"
	"go-caipu/pkg/services/pluginsintegration/plugincontext"
	"go-caipu/pkg/services/schedule"
	"os"

	"go-caipu/pkg/api/middleware"
	"go-caipu/pkg/api/pendingFeatureCode"
	"go-caipu/pkg/api/plugin"
	"go-caipu/pkg/api/staticRoutes"
	"go-caipu/pkg/bus"
	"go-caipu/pkg/infra/log"
	"go-caipu/pkg/plugins"
	"go-caipu/pkg/services/admin/auth"
	"go-caipu/pkg/services/admin/config"
	"go-caipu/pkg/services/admin/dept"
	"go-caipu/pkg/services/admin/dict"
	"go-caipu/pkg/services/admin/menu"
	"go-caipu/pkg/services/admin/post"
	"go-caipu/pkg/services/admin/role"
	"go-caipu/pkg/services/admin/sysapi"
	"go-caipu/pkg/services/admin/sysuser"
	"go-caipu/pkg/services/datasources"
	storage "go-caipu/pkg/services/storage/service"
	"go-caipu/pkg/services/tools"
	"go-caipu/pkg/setting"
	"net"
	"net/http"
	"strings"
	"sync"
)

type HTTPServer struct {
	log           log.Logger
	context       context.Context
	httpSrv       *http.Server
	Cfg           *setting.Cfg
	Listener      net.Listener
	handle        *gin.Engine
	Snowflake     *snowflake.Node
	bus           bus.Bus
	Jwt           *jwt.JWT
	userService   sysuser.Service
	loginService  auth.Authenticator
	menuService   menu.Service
	roleService   role.Service
	deptService   dept.Service
	postService   post.Service
	apiService    sysapi.Service
	toolService   tools.Service
	dictService   dict.Service
	configService config.Service
	//plugins
	pluginStore           plugins.Store
	pluginClient          plugins.Client
	pluginInstaller       plugins.Installer
	pluginFileStore       plugins.FileStore
	dataSourceService     datasources.Service
	systemLogService      systemlog.Service
	pluginContextProvider *plugincontext.Provider
	scheduleService       schedule.Service
	storageService        *storage.StorageService
	onlineService         onlineuser.Service
}
type ServerOptions struct {
	Listener net.Listener
}

func ProvideHTTPServer(cfg *setting.Cfg, bus bus.Bus, userService sysuser.Service,
	loginService auth.Authenticator,
	menuService menu.Service,
	roleService role.Service,
	deptService dept.Service,
	postService post.Service,
	apiService sysapi.Service, toolService tools.Service, dictService dict.Service,
	configService config.Service,
	pluginStore plugins.Store,
	pluginClient plugins.Client,
	pluginInstaller plugins.Installer,
	pluginFileStore plugins.FileStore,
	dataSourceService datasources.Service, systemLogService systemlog.Service,
	pluginContextProvider *plugincontext.Provider,
	scheduleService schedule.Service,
	storageService *storage.StorageService,
	onlineService onlineuser.Service,
) (*HTTPServer, error) {
	hs := &HTTPServer{
		Cfg:                   cfg,
		log:                   log.New("http.server"),
		bus:                   bus,
		userService:           userService,
		loginService:          loginService,
		menuService:           menuService,
		roleService:           roleService,
		deptService:           deptService,
		postService:           postService,
		apiService:            apiService,
		toolService:           toolService,
		dictService:           dictService,
		configService:         configService,
		pluginStore:           pluginStore,
		pluginClient:          pluginClient,
		pluginInstaller:       pluginInstaller,
		pluginFileStore:       pluginFileStore,
		dataSourceService:     dataSourceService,
		systemLogService:      systemLogService,
		pluginContextProvider: pluginContextProvider,
		scheduleService:       scheduleService,
		storageService:        storageService,
		onlineService:         onlineService,
	}
	hs.Jwt = jwt.NewJwt(cfg.Jwt.Secret, cfg.Jwt.Timeout)
	//create snowflake
	node, _ := snowflake.NewNode(cfg.Application.Node)

	hs.Snowflake = node

	ProvideCaptchaService(hs.Cfg)
	if hs.Listener != nil {
		hs.log.Debug("Using provided listener")
	}
	return hs, nil
}

// applyRoutes 注册通用路由
func (hs *HTTPServer) applyRoutes() {
	//register pending feature code
	pendingFeatureCode.RegisterPendingRoutes(hs.handle)

}

// addMiddlewaresAndStaticRoutes 绑定中间件
func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
	m := hs.handle
	m.Use(middleware.DemoEvnHandler(hs.Cfg))
	m.Use(middleware.LoggerToFileMiddleware(hs.bus, hs.Jwt))
	m.Use(middleware.CORSMiddleware(hs.Cfg))
	m.Use(i18n.UseI18nHandler())
	m.Use(middleware.ZapRecovery(hs.log))
}
func (hs *HTTPServer) Run(ctx context.Context) error {
	hs.context = ctx
	hs.handle = gin.Default()
	staticRoutes.SetupStaticRoutes(hs.handle)
	hs.addMiddlewaresAndStaticRoutes()
	//注册路由
	hs.registerRoutes()
	plugin.RegisterPluginRoutes(hs.handle, hs.context)
	//注册通用路由
	hs.applyRoutes()
	staticRoutes.EndNoRoute(hs.handle)

	//此处ReadTimeOut添加，会有偶发故障
	host := strings.TrimSuffix(strings.TrimPrefix(hs.Cfg.Application.HTTPAddr, "["), "]")
	port := hs.Cfg.Application.HTTPPort
	if hs.Cfg.Application.EnabledSSL {
		port = "443"
	}
	hs.httpSrv = &http.Server{
		Addr:    net.JoinHostPort(host, port),
		Handler: hs.handle,
	}

	var wg sync.WaitGroup
	wg.Add(1)
	// handle http shutdown on server context done
	go func() {
		defer wg.Done()
		<-ctx.Done()
		if err := hs.httpSrv.Shutdown(context.Background()); err != nil {
			hs.log.Error("Failed to shutdown server", "error", err)
		}
	}()
	if hs.Cfg.Application.EnabledSSL {
		certFile := "server.crt"
		keyFile := "server.key"
		if _, err := os.Stat(certFile); os.IsNotExist(err) {
			panic(fmt.Sprintf("Certificate file %s does not exist", certFile))
		}
		if _, err := os.Stat(keyFile); os.IsNotExist(err) {
			panic(fmt.Sprintf("Private key file %s does not exist", keyFile))
		}
		hs.log.Info("HTTPS Server Listen", "address", hs.Cfg.Application.HTTPAddr+":443", "protocol", "https")
		if err := hs.httpSrv.ListenAndServeTLS(certFile, keyFile); err != nil {
			panic("Failed to start HTTPS server")
		}
	}
	hs.log.Info("HTTP Server Listen", "address", hs.Cfg.Application.HTTPAddr+":"+hs.Cfg.Application.HTTPPort, "protocol", "http")
	if err := hs.httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		hs.log.Error("Failed to shutdown server", "error", err)
		return nil
	}

	wg.Wait()

	return nil
}
